package embyparse;

import java.io.File;
import java.util.ArrayList;

import emby.ASTNode;
import emby.EmbyRuntime;
import emby.err.InternalError;
import emby.err.RestrictedAccessSyntaxError;
import emby.err.SyntaxError;
import emby.expr.Call;
import emby.expr.DotVariable;
import emby.expr.Expression;
import emby.expr.GenericBlock;
import emby.expr.IndexVariable;
import emby.expr.LambdaExpression;
import emby.expr.ListExpression;
import emby.expr.Literal;
import emby.expr.OperatorCall;
import emby.expr.Variable;
import emby.lang.Block;
import emby.lang.EmbyFunction;
import emby.lang.Scope;
import emby.stmt.Assignment;
import emby.stmt.CompoundAssignment;
import emby.stmt.DefineFunction;
import emby.stmt.EnhancedFor;
import emby.stmt.For;
import emby.stmt.If;
import emby.stmt.IfElse;
import emby.stmt.Return;
import emby.stmt.Statement;
import emby.stmt.Throw;
import emby.stmt.TryCatch;
import emby.stmt.While;
import embyparse.noobparser.Pattern;
import embyparse.parsetree.LiteralNode;
import embyparse.parsetree.ParseNode;
import embyparse.tokenizer.Tokenizer;
import embyparse.util.TokenStream;

/**
 * This class takes parse tree nodes and converts them into AST nodes.
 * @author Justin
 *
 */
public class TreeToCode {

	public static void main(String[] args){
		TokenStream t = (new Tokenizer(new File("res//tests.emby")));
		Parser p = new Parser(t);
		p.parse();

		ASTNode[] parsed = treeToNodes(p.parsestack);
		Block b= new Block(parsed);
		EmbyRuntime.echo=false;
		b.eval(new Scope());
	}

	/**
	 * Converts multiple parse nodes into AST nodes.
	 * @param parseTree
	 * @return an array of ASTNodes
	 */
	public static ASTNode[] treeToNodes(ArrayList<ParseNode> parseTree){
		ArrayList<ASTNode> ast = new ArrayList<ASTNode>();

		for(int i = 0; i<parseTree.size();i++){
			ASTNode node = nodeConvert( parseTree.get(i));
			if(node!=null){
			ast.add( node );
			}
			//ast[i]=nodeConvert(parseTree.get(i));
		}

		ASTNode[] astarray = new ASTNode[ast.size()];
		for(int i = 0; i<ast.size();i++){
			astarray[i] = ast.get(i);
		}
		return astarray;
	}
	/**
	 * Converts a single parse node into an ASTNode
	 * @param parsenode
	 * @return an ASTNode
	 */
	public static ASTNode nodeConvert(ParseNode parsenode){
		Pattern tag = parsenode.tag;
		if( tag.equalTag("STMT")){//statments
			return parseStatement(parsenode);
		}
		else if (tag.equalTag("EXPR")){//expressions
			return parseExpr(parsenode);
		}
		else if(tag.equalTag("COMMENT")){
			return null;
		}else if (tag.equalTag(";")){
			return null;
		}
		else{
			throw new SyntaxError(parsenode.lineNumber,"unknown symbol: "+tag);
		}
		//return null;
	}
	public static  Statement parseStatement(ParseNode parsenode){
		Pattern tag = parsenode.tag;
		if( tag.equalTag("RET_STMT")){
			return parseReturn(parsenode);
		}
		else if (tag.equalTag("ASSIGN_STMT")){
			return parseAssign(parsenode);
		}
		else if (tag.equalTag("DEF_STMT")){
			return parseDef(parsenode);
		}
		else if (tag.equalTag("WHILE_STMT")){
			return parseWhile(parsenode);
		}
		else if (tag.equalTag("IF_STMT")){
			return parseIf(parsenode);
		}
		else if (tag.equalTag("IFELSE")){
			return parseIfelse(parsenode);
		}
		else if (tag.equalTag("FOR_STMT")){
			return parseFor(parsenode);
		}
		else if (tag.equalTag("TRYCATCH")){
			return parseTrycatch(parsenode);
		}
		else if (tag.equalTag("THROW_STMT")){
			return parseThrow(parsenode);
		}
		throw new InternalError(parsenode.lineNumber,"Unknown expression tag: "+tag);
		//return null;
	}
	public static Expression parseExpr(ParseNode parsenode){
		Pattern tag = parsenode.tag;
		if( tag.equalTag("LITERAL")){
			return parseLiteral(parsenode);
		}
		if( tag.equalTag("LIST_EXPRESSION")){
			return parseListExpression(parsenode);
		}
		if( tag.equalTag("VAR")){
			return parseVar(parsenode);
		}
		if(tag.equalTag("CALL")){
			return parseCall(parsenode);
		}
		if( tag.equalTag("PAREN")){
			return parseParenExpr(parsenode);
		}
		if( tag.equalTag("LAMBDA")){
			return parseLambda(parsenode);
		}
		if( tag.equalTag("GEN_BLOCK")){
			return parseGenBlock(parsenode);
		}
		throw new InternalError(parsenode.lineNumber,"Unknown expression tag: "+tag);
		//return null;
	}

	public static Return parseReturn(ParseNode parsenode){
		//public Return(Expression exp)
		ParseNode ret = parsenode.children.get(0);
		if( !ret.tag.equalTag("return")){
			throw new InternalError(parsenode.lineNumber,"Malformed return");
		}
		ParseNode expr = parsenode.children.get(1);

		if( parsenode.children.size()!=2){
			throw new InternalError(parsenode.lineNumber,"Malformed return");
		}

		Return astnode = new Return(parseExpr(expr));
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static Assignment parseAssign(ParseNode parsenode){
		//public Assignment(Variable var, Expression val){
		if( parsenode.children.size()!=3){
			throw new InternalError("Malformed assignment");
		}
		ParseNode var = parsenode.children.get(0);
		if( !var.tag.equalTag("VAR")){
			throw new InternalError(parsenode.lineNumber,"Malformed asignment");
		}
		ParseNode expr = parsenode.children.get(2);
		if( !expr.tag.equalTag("EXPR")){
			throw new InternalError("Malformed asignment");
		}
		Variable v = parseVar(var);
		if( v.isReadOnly()){
			throw new RestrictedAccessSyntaxError(parsenode.lineNumber,v.getName()+" has restricted access and cannot be reassigned.");
		}
		
		ParseNode operator = parsenode.children.get(1);
		if( operator.tag.equalTag("COMPOP")){
			return parseCompoundAssignment(v, parseExpr(expr), operator.tag.getName(), parsenode);
		}
		
		Assignment astnode = new Assignment(v,parseExpr(expr));
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static CompoundAssignment parseCompoundAssignment(Variable v, Expression e, String operator, ParseNode p) {
		EmbyFunction op = OperatorCall.toFunction(operator);
		CompoundAssignment astnode = new CompoundAssignment(v, op, e);
		astnode.setLine(p.lineNumber);
		return astnode;
	}
	public static DefineFunction parseDef(ParseNode parsenode){
		//public DefineFunction(String fname, Block _body, Variable... parameters){
		//CompoundPattern("DEF_STMT","fn","VAR","PAREN","BLOCK")
		//  new CompoundPattern("DEF_STMT","DEF_INCOMPLETE","BLOCK")
		// new CompoundPattern(9,"DEF_INCOMPLETE","fn","FNCALL")
		// "FNCALL","EXPR","PAREN")
		if( parsenode.children.size()!=2){
			throw new InternalError(parsenode.lineNumber,"Malformed function");
		}

		ParseNode def_i = parsenode.children.get(0);
		if( !def_i.tag.equalTag("DEF_INCOMPLETE")){
			throw new InternalError(parsenode.lineNumber,"Malformed function");
		}

		if( def_i.children.size() != 2){
			throw new InternalError(parsenode.lineNumber,"Malformed def_incomplete");
		}
		ParseNode fncall = def_i.children.get(1);
		if(! fncall.tag.equalTag("FNCALL")){
			throw new InternalError(parsenode.lineNumber,"Malformed function");
		}


		ParseNode name = fncall.children.get(0);

		if( !name.tag.equalTag("VAR")){
			throw new InternalError(parsenode.lineNumber,"Malformed function");
		}
		Variable v = parseVar(name);
		if( v.isReadOnly()){
			throw new RestrictedAccessSyntaxError(parsenode.lineNumber,v.getName()+" has restricted access and cannot be the name of a defined function.");
		}
		String fnName = v.getName();

		ParseNode params = fncall.children.get(1);
		if( !params.tag.equalTag("PAREN")){
			throw new InternalError(parsenode.lineNumber,"Malformed function");
		}
		Variable[] parameters = parseParenParams(params);

		ParseNode body = parsenode.children.get(1);
		if( !body.tag.equalTag("BLOCK")){
			throw new InternalError(parsenode.lineNumber,"Malformed function definition");
		}
		ASTNode[] bodynodes = treeToNodes(body.children);
		Block _body = new Block(bodynodes);

		DefineFunction astnode= new DefineFunction(fnName, _body, parameters);
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static While parseWhile(ParseNode parsenode){
		//Expression _condition, Block _body){
		//"while","PAREN","BLOCK"
		if( parsenode.children.size()!=3){
			throw new InternalError(parsenode.lineNumber,"Malformed while");
		}

		ParseNode condition = parsenode.children.get(1);
		if( !condition.tag.equalTag("EXPR")){
			throw new InternalError(parsenode.lineNumber,"Malformed while");
		}
		Expression e = parseExpr(condition);

		ParseNode body = parsenode.children.get(2);
		if( !body.tag.equalTag("BLOCK")){
			throw new InternalError(parsenode.lineNumber,"Malformed while");
		}
		ASTNode[] bodynodes = treeToNodes(body.children);
		Block _body = new Block(bodynodes);

		return new While(e,_body);
	}
	public static If parseIf(ParseNode parsenode){
		if( parsenode.children.size()!=3){
			throw new InternalError(parsenode.lineNumber,"Malformed if");
		}

		ParseNode condition = parsenode.children.get(1);
		if( !condition.tag.equalTag("EXPR")){
			throw new InternalError(parsenode.lineNumber,"Malformed if");
		}
		Expression e = parseExpr(condition);

		ParseNode body = parsenode.children.get(2);
		if( !body.tag.equalTag("BLOCK")){ //if the body is not enclosed in braces
																		//and is actually a single expr or stmt
			Block _body =convertToBlock(body);
			return new If(e,_body);
		}
		ASTNode[] bodynodes = treeToNodes(body.children);
		Block _body = new Block(bodynodes);
		If node = new If(e,_body);
		node.setLine(parsenode.lineNumber);
		return node;
	}
	public static Block parseElse(ParseNode elseNode){
		if( elseNode.children.size()!=2){
			throw new InternalError(elseNode.lineNumber,"Malformed else");
		}

		ParseNode body = elseNode.children.get(1);
		if( !body.tag.equalTag("BLOCK")){
			return convertToBlock(body);
		}
		ASTNode[] bodynodes = treeToNodes(body.children);
		Block _body = new Block(bodynodes);
		return _body;
	}
	public static IfElse parseIfelse(ParseNode parsenode){
		if( parsenode.children.size()!=2){
			throw new InternalError(parsenode.lineNumber,"Malformed if");
		}

		ParseNode _if = parsenode.children.get(0);
		if( !_if.tag.equalTag("IF_STMT")){
			throw new InternalError(parsenode.lineNumber,"Malformed if else");
		}
		If arg1 = parseIf(_if);
		ParseNode _else = parsenode.children.get(1);
		Block elsebody = parseElse(_else);
		IfElse node = new IfElse(arg1.condition,arg1.body,elsebody);
		node.setLine(parsenode.lineNumber);
		return node;
	}
	public static Statement parseFor(ParseNode parsenode){
		// For(ASTNode initializer, Expression _condition, ASTNode accumulator, Block _body){
		//"for","PAREN","BLOCK"
		if( parsenode.children.size()!=3){
			throw new InternalError(parsenode.lineNumber,"Malformed for");
		}
		ParseNode body = parsenode.children.get(2);
		if( !body.tag.equalTag("BLOCK")){
			throw new InternalError(parsenode.lineNumber,"Malformed for");
		}
		ASTNode[] bodynodes = treeToNodes(body.children);
		Block _body = new Block(bodynodes);
		ParseNode paren = parsenode.children.get(1);
		if( !paren.tag.equalTag("PAREN")){
				throw new InternalError(parsenode.lineNumber,"Malformed for");
		}
		if( paren.children.size()==5){
			return parseNormalFor(parsenode, paren, _body);
		}else if( paren.children.size()==3){
			return parseEnhancedFor(parsenode, paren, _body);
		}else{
			throw new SyntaxError(parsenode.lineNumber,"Expected (Initializer;Condition;Accumulator) or (Var:Iterator) on For statement.");
		}
	}
	public static For parseNormalFor(ParseNode parsenode, ParseNode paren, Block _body){
		// For(ASTNode initializer, Expression _condition, ASTNode accumulator, Block _body){
				//"for","PAREN","BLOCK"
	{//parse the inner paren
		if( paren.children.size()!=5){
			throw new SyntaxError(parsenode.lineNumber,"Expected (Initializer;Condition;Accumulator) on For statement. MUST have all 3 divided by semicolons.");
		}
		if((!(paren.children.get(1).tag.equalTag(";")))&&(!(paren.children.get(3).tag.equalTag(";")))){
			throw new SyntaxError(parsenode.lineNumber,"For statement must be divided by semicolons");
		}
		ASTNode initializer = nodeConvert( paren.children.get(0));
		ParseNode condition = paren.children.get(2);
		if( !condition.tag.equalTag("EXPR")){
			throw new InternalError(parsenode.lineNumber,"Condition of for loop must be an expression");
		}
		Expression e = parseExpr(condition);

		ASTNode accumulator = nodeConvert(paren.children.get(4));
		For node = new For(initializer,e,accumulator,_body);
		node.setLine(parsenode.lineNumber);
		return node;
	}
	}
	public static EnhancedFor parseEnhancedFor(ParseNode parsenode, ParseNode paren, Block _body){
		// ( var : iterator )
		if ( !paren.children.get(1).tag.equalTag(":")){
			throw new SyntaxError(parsenode.lineNumber,"Enhanced for statement must be divided by a colon");
		}
		ParseNode varNode = paren.children.get(0);
		if( !varNode.tag.equalTag("VAR")){
			throw new InternalError(parsenode.lineNumber,"Var of enhanced for not a var");
		}
		Variable var = parseVar(varNode);
		ParseNode iterNode = paren.children.get(2);
		if( !varNode.tag.equalTag("EXPR")){
			throw new InternalError(parsenode.lineNumber,"Iterator of enhanced for not an expression");
		}
		Expression iter = parseExpr(iterNode);
		EnhancedFor efor = new EnhancedFor(var, iter, _body);
		efor.setLine(parsenode.lineNumber);
		return  efor;
	}

	public static TryCatch parseTrycatch(ParseNode parsenode){
		//CompoundPattern("TRY_CLAUSE","try","BLOCK")
		//CompoundPattern("CATCH_CLAUSE","catch","PAREN","BLOCK"
		//CompoundPattern("TRYCATCH","TRY_CLAUSE","CATCH_CLAUSE")
		if(parsenode.children.size()!= 2){
			throw new InternalError(parsenode.lineNumber, "Malformed trycatch");
		}
		ParseNode Try = parsenode.children.get(0);
		if((!Try.tag.equalTag("TRY_CLAUSE")) || Try.children.size()!=2 ){
			throw new InternalError(parsenode.lineNumber, "Malformed try clause");
		}
		ParseNode Catch = parsenode.children.get(1);
		if((!Catch.tag.equalTag("CATCH_CLAUSE")) || Catch.children.size()!=3 ){
			throw new InternalError(parsenode.lineNumber, "Malformed catch clause");
		}
		Block tryBlock = parseBlock(Try.children.get(1));
		Block catchBlock = parseBlock(Catch.children.get(2));
		Variable[] catchVar = parseParenParams(Catch.children.get(1));
		if( catchVar.length!=1){
			throw new SyntaxError(Catch.lineNumber,"Catch has an invalid number of variable parameters. Should be 1");
		}
		TryCatch astnode= new TryCatch(tryBlock, catchVar[0], catchBlock);
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static Throw parseThrow(ParseNode parsenode){
		//public throw(Expression exp)
		if( parsenode.children.size()!=2){
			throw new InternalError(parsenode.lineNumber,"Malformed throw");
		}
		ParseNode ret = parsenode.children.get(0);
		if( !ret.tag.equalTag("throw")){
			throw new InternalError(parsenode.lineNumber,"Malformed throw");
		}
		ParseNode expr = parsenode.children.get(1);

		Throw astnode = new Throw(parseExpr(expr));
		astnode.setLine(parsenode.lineNumber);

		return astnode;
	}

	public static ListExpression parseListExpression(ParseNode parsenode){ //DONE
		if( !parsenode.tag.equalTag("LIST_EXPRESSION")){
			throw new InternalError(parsenode.lineNumber,"Malformed list expression");
		}
		ASTNode[] listnodes = treeToNodes(parsenode.children);
		Expression[] exprs = new Expression[listnodes.length];
		for(int i = 0; i<exprs.length; i++){
			if( listnodes[i] instanceof Expression){
				exprs[i] = (Expression)listnodes[i];
			}else{
				throw new SyntaxError(parsenode.lineNumber,"Non-expression found within list literal: "+listnodes[i]);
			}
		}
		ListExpression astnode = new ListExpression(exprs);
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static Literal parseLiteral(ParseNode parsenode){ //DONE
		//public Literal(Object value)
		Literal astnode = new Literal(  ((LiteralNode)parsenode).getVal());
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static Variable parseVar(ParseNode parsenode){ //DONE
		Pattern tag = parsenode.tag;
		if(tag.equalTag("DOTCALL")){
			return parseDotVar(parsenode);
		}
		if(tag.equalTag("INDEXCALL")){
			return parseIndex(parsenode);
		}

		Variable astnode =  new Variable( parsenode.value.getString());
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static IndexVariable parseIndex(ParseNode parsenode){
		//IndexVariable(Expression arg1, Expression index){
		if( parsenode.children.size()!=2){
			throw new InternalError(parsenode.lineNumber,"Malformed index");
		}
		ParseNode arg1 = parsenode.children.get(0);
		if( !arg1.tag.equalTag("EXPR")){
			throw new InternalError(parsenode.lineNumber,"Malformed index");
		}
		Expression e1 = parseExpr(arg1);

		ParseNode square = parsenode.children.get(1);
		if( !square.tag.equalTag("INDEX")){
			throw new InternalError(parsenode.lineNumber,"Malformed index");
		}

		if(square.children.size()!=3){
			throw new SyntaxError(parsenode.lineNumber,"Expected only 1 expression within square brackets");
		}

		ParseNode arg2= square.children.get(1);
		if( !arg2.tag.equalTag("EXPR")){
			throw new InternalError(parsenode.lineNumber,"Malformed index");
		}
		Expression e2 = parseExpr(arg2);

		IndexVariable astnode = new IndexVariable(e1,e2);
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static DotVariable parseDotVar(ParseNode parsenode){
		if( parsenode.children.size()!=3){
			throw new InternalError(parsenode.lineNumber,"Malformed dot expression");
		}
		ParseNode arg1 = parsenode.children.get(0);
		if( !arg1.tag.equalTag("EXPR")){
			throw new InternalError(+parsenode.lineNumber,"Malformed dot expression");
		}
		Expression e1 = parseExpr(arg1);

		ParseNode arg2 = parsenode.children.get(2);
		if( !arg2.tag.equalTag("VAR")){
			throw new InternalError(+parsenode.lineNumber,"Malformed dot expression");
		}
		Variable v = parseVar(arg2);
		DotVariable astnode =  new DotVariable(e1,v);
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	public static LambdaExpression parseLambda(ParseNode parsenode){
		//("LAMBDA","PAREN","=>","ALL")
		//"LAMBDA","PAREN","=>","BLOCK")
		if( parsenode.children.size()!=3){
			throw new InternalError(parsenode.lineNumber,"Malformed lambda");
		}

		ParseNode params = parsenode.children.get(1);
		if( !params.tag.equalTag("PAREN")){
			throw new InternalError(parsenode.lineNumber,"Malformed function");
		}
		Variable[] parameters = parseParenParams(params);

		ParseNode bodyOrAll = parsenode.children.get(2);

		Block _body = null;
		if( bodyOrAll.tag.equalTag("BLOCK")){
			_body = parseBlock(bodyOrAll);
		}
		else if ( bodyOrAll.tag.equalTag("EXPR")){
			//System.out.println("Expression lambda");
			Expression body = parseExpr(bodyOrAll);
			Return r = new Return(body);
			r.setLine(body.getLine());
			_body = new Block(r);
		}else{
			throw new InternalError(parsenode.lineNumber,"Malformed lambda expression");
		}

		LambdaExpression astnode= new LambdaExpression( _body, parameters);
		astnode.setLine(parsenode.lineNumber);
		return astnode;
	}
	/*
	public static NewOperator parseNew(ParseNode parsenode){
		// "new","FNCALL"
		if( parsenode.children.size()!=2){
			throw new InternalError(parsenode.lineNumber,"Malformed new");
		}
		ParseNode fncall = parsenode.children.get(1);
		if( ! fncall.tag.equalTag("FNCALL")){
			throw new InternalError(parsenode.lineNumber,"Malformed new");
		}

		ParseNode className = fncall.children.get(0);
		if( ! className.tag.equalTag("VAR")){
			throw new InternalError(parsenode.lineNumber,"Malformed new. Only variable names are allowed for the constructor name call.");
		}
		String clsName = (String) (parseVar(className)).getName();
		ParseNode constArgs = fncall.children.get(1);
		if( ! constArgs.tag.equalTag("PAREN")){
			throw new InternalError(parsenode.lineNumber,"Malformed new");
		}
		Expression[] constructorArgs = parseParenArgs(constArgs);
		NewOperator n = new NewOperator(clsName, constructorArgs);
		n.setLine(parsenode.lineNumber);
		return n;
	}*/
	public static GenericBlock parseGenBlock(ParseNode parsenode){
		Expression wrapper;
		Expression[] operands;
		Variable[] blockargs;
		Block body;
		if( (parsenode.children.size()!=3)){
			throw new InternalError(parsenode.lineNumber,"Malformed generic block");
		}
		
		
		if( parsenode.children.get(1).tag.equalTag("FNCALL")){
			//throw new InternalError(parsenode.lineNumber,"FNCALL genblock Not implemented");
			ParseNode fncallNode = parsenode.children.get(1);
			ParseNode wrapper_expr = fncallNode.children.get(0);
			wrapper = parseExpr(wrapper_expr);
			ParseNode parenNode = fncallNode.children.get(1);
			
			int indexOfSemicolon = Integer.MAX_VALUE;
			//ArrayList<ParseNode> parenContents = new ArrayList<ParseNode>();
			for(int i=0; i<parenNode.children.size(); i++){
				ParseNode contents = parenNode.children.get(i);
				if( contents.tag.equalTag(";")){
					if( indexOfSemicolon ==  Integer.MAX_VALUE){
						indexOfSemicolon = i;
					}else{
						throw new SyntaxError(parsenode.lineNumber,"Found 2 semicolons in Generic Block. Required format: (expr...; args...)");
					}
					
				}else{
					//parenContents.add(contents);
				}
			}
		
			
			int operandIndex = Math.min(indexOfSemicolon, parenNode.children.size());
			operands = new Expression[operandIndex];
			for(int i=0; i< operandIndex; i++){
				operands[i] = parseExpr(parenNode.children.get(i));
			}
			//System.out.println("OpIndex: "+operandIndex);
			//System.out.println("Tot: "+parenNode.children.size());
			if( indexOfSemicolon != Integer.MAX_VALUE){
				operandIndex++;
			}
			blockargs = new Variable[parenNode.children.size()-operandIndex];
			for(int i=operandIndex; i< parenNode.children.size(); i++){
				blockargs[i-operandIndex] = parseVar(parenNode.children.get(i));
			}
			
			
		}else if( parsenode.children.get(1).tag.equalTag("EXPR")) { //parsenode.children.size()==4
			ParseNode wrapper_expr = parsenode.children.get(1);
			wrapper = parseExpr(wrapper_expr);
			operands = new Expression[0];
			blockargs = new Variable[0];
		}else{
			throw new InternalError(parsenode.lineNumber,"Malformed generic block");
		}
		
		ParseNode _body = parsenode.children.get(2);
		if( !_body.tag.equalTag("BLOCK")){
			throw new InternalError(parsenode.lineNumber,"Malformed generic block");
		}
		ASTNode[] bodynodes = treeToNodes(_body.children);
		body = new Block(bodynodes);
		
		return new GenericBlock(wrapper,operands,blockargs,body);
		//GenericBlock(Expression wrapper_fn,Expression[] operands,Variable[] block_args, Block body)
	}
	
	public static Expression parseParenExpr(ParseNode parsenode){
		if( parsenode.children.size()!=1){
			throw new SyntaxError(parsenode.lineNumber,"Must have onl 1 expression within parentheses");
		}
		ParseNode expr = parsenode.children.get(0);
		return parseExpr(expr);
	}
	public static Expression[] parseParenArgs(ParseNode parsenode){

		ASTNode[] argElements = treeToNodes(parsenode.children);
		Expression[] args = new Expression[argElements.length];

		for(int i = 0; i<args.length;i++){
			if( argElements[i] instanceof Expression){
				args[i]= (Expression)argElements[i];
			}else{
				throw new SyntaxError(parsenode.lineNumber,argElements[i]+" is not an expression");
			}
		}
		return args;
	}
	public static Variable[] parseParenParams(ParseNode parsenode){
		ASTNode[] paramElements = treeToNodes(parsenode.children);
		Variable[] params = new Variable[paramElements.length];
		for(int i = 0; i<params.length;i++){
			if( paramElements[i] instanceof Expression){
				params[i]= (Variable)paramElements[i];
			}else{
				throw new SyntaxError(parsenode.lineNumber,paramElements[i]+" is not an valid variable within function parameter definition.");
			}
		}
		return params;
	}
	public static Block parseBlock(ParseNode parsenode){
		if(! parsenode.tag.equalTag("BLOCK")){
			throw new InternalError(parsenode.lineNumber, "Bad Block");
		}
		ASTNode[] bodynodes = treeToNodes(parsenode.children);
		Block _body = new Block(bodynodes);
		_body.setLine(parsenode.lineNumber);
		return _body;
	}
	/**
	 * Wraps a parsenode by itself inside a block
	 * @param body
	 * @return
	 */
	public static Block convertToBlock(ParseNode body){
		ASTNode bodynode = nodeConvert(body);
		ASTNode[] bodynodes = {bodynode};
		Block _body = new Block(bodynodes);
		return _body;
	}
	public static Call parseCall(ParseNode parsenode){
		Pattern tag = parsenode.tag;
		if( tag.equalTag("FNCALL")){
			ParseNode operator = parsenode.children.get(0);
			if( !operator.tag.equalTag("EXPR")){
				throw new InternalError(parsenode.lineNumber,"Malformed function call");
			}
			ParseNode operands = parsenode.children.get(1);
			if( !operands.tag.equalTag("PAREN")){
				throw new InternalError(parsenode.lineNumber,"Malformed function call");
			}
			if( parsenode.children.size()!=2){
				throw new InternalError(parsenode.lineNumber,"Malformed function call");
			}
			Call astnode =  new Call(  parseExpr(operator), parseParenArgs(operands));
			astnode.setLine(parsenode.lineNumber);
			return astnode;
		}
		else if(tag.equalTag("INFIXCALL")){
			if( parsenode.children.size()!=3){
				throw new InternalError(parsenode.lineNumber,"Malformed infix call");
			}
			ParseNode arg1 = parsenode.children.get(0);
			if( !arg1.tag.equalTag("EXPR")){
				throw new InternalError(parsenode.lineNumber,"Malformed infix call");
			}
			Expression e1 = parseExpr(arg1);
			ParseNode arg2 = parsenode.children.get(2);
			if( !arg2.tag.equalTag("EXPR")){
				throw new InternalError(parsenode.lineNumber,"Malformed infix call");
			}
			Expression e2 = parseExpr(arg2);
			ParseNode operator = parsenode.children.get(1);
			if( !operator.tag.equalTag("INFIXOP")){
				throw new InternalError(parsenode.lineNumber,"Malformed infix call");
			}
			String op = operator.tag.getName();
			OperatorCall astnode = new OperatorCall(op,e1,e2);
			astnode.setLine(parsenode.lineNumber);
			return astnode;
		}
		else if(tag.equalTag("PREFIXCALL")){
			ParseNode operator = parsenode.children.get(0);
			if( !operator.tag.equalTag("PREFIXOP")){
				throw new InternalError(parsenode.lineNumber,"Malformed prefix call");
			}
			String op = operator.tag.getName();
			ParseNode arg1 = parsenode.children.get(1);
			if( !arg1.tag.equalTag("EXPR")){
				throw new InternalError(+parsenode.lineNumber,"Malformed prefix call");
			}
			Expression e1 = parseExpr(arg1);

			OperatorCall astnode = new OperatorCall(op,e1);
			astnode.setLine(parsenode.lineNumber);
			return astnode;
		}else if(tag.equalTag("POSTFIXCALL")){
			ParseNode operator = parsenode.children.get(1);
			if( !operator.tag.equalTag("POSTFIXOP")){
				throw new InternalError(parsenode.lineNumber,"Malformed postfix call");
			}
			String op = operator.tag.getName();
			ParseNode arg1 = parsenode.children.get(0);
			if( !arg1.tag.equalTag("EXPR")){
				throw new InternalError(+parsenode.lineNumber,"Malformed postfix call");
			}
			//Expression e1 = parseExpr(arg1);

			throw new InternalError("The operator: "+op+" is not implemented yet");
			//astnode.setLine(parsenode.lineNumber);
			//return astnode;
		}
		else{
			throw new SyntaxError(parsenode.lineNumber,"Unknown Call expression tag: "+tag);
		}
	}

}
